using System;
using System.Collections.Generic;
using UnityEngine;
using Object = UnityEngine.Object;

namespace ZFramework.Runtime
{
    public partial class EntityMgr : FrameworkManagerBase
    {
        private Dictionary<int, EntityInfo> _entityInfos;
        private Dictionary<string, EntityGroup> _entityGroups; //分组
        private Dictionary<int, int> _entitiesBeLoading; //加载中的
        private HashSet<int> _entities2ReleaseOnLoad; //加载完成后就卸载的
        private LoadAssetCallbacks _loadAssetCallbacks;
        private Queue<EntityInfo> _recycleQueue;
        private int _serialId; //真正加载一次资源，这个值才会+1

        private IEventManager _eventMgr;
        private IResourceMgr _resourceMgr;
        private ObjectPoolMgr _objectPoolMgr;

        protected override void Awake()
        {
            base.Awake();
            _serialId = 0;
            _entityInfos = new Dictionary<int, EntityInfo>();
            _entityGroups = new Dictionary<string, EntityGroup>();
            _entitiesBeLoading = new Dictionary<int, int>();
            _entities2ReleaseOnLoad = new HashSet<int>();
            _loadAssetCallbacks = new LoadAssetCallbacks(LoadAssetSuccessCallback, LoadAssetFailureCallback);
            _recycleQueue = new Queue<EntityInfo>();
        }

        void Start()
        {
            _eventMgr = FrameworkManagerManager.GetManager<EventManager>();
            if (_eventMgr == null)
            {
                Log.Error("EventMgr is invalid.");
                return;
            }
            _resourceMgr = FrameworkManagerManager.GetManager<YooResourceMgr>();
            if (_resourceMgr == null)
            {
                Log.Error("YooResourceMgr is invalid.");
                return;
            }
            _objectPoolMgr = FrameworkManagerManager.GetManager<ObjectPoolMgr>();
            if (_objectPoolMgr == null)
            {
                Log.Error("ObjectPoolMgr is invalid.");
                return;
            }
        }

        public override void Shutdown()
        {

        }

        public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
        {
            while (_recycleQueue.Count > 0)
            {
                EntityInfo entityInfo = _recycleQueue.Dequeue();
                Entity entity = entityInfo.Entity;
                EntityGroup entityGroup = (EntityGroup) entity.EntityGroup;
                if (entityGroup == null)
                {
                    throw new Exception("entity group is invalid");
                }

                entityInfo.Status = EntityStatus.WillRecycle;
                entity.OnRecycle();
                entityInfo.Status = EntityStatus.Recycled;
                entityGroup.UnSpawnEntity(entity);
                ReferencePool.Release(entityInfo);
            }

            foreach (var item in _entityGroups)
            {
                item.Value.OnUpdate(elapseSeconds, realElapseSeconds);
            }
        }

        public bool AddEntityGroup(string entityGroupName)
        {
            if (string.IsNullOrEmpty(entityGroupName))
            {
                return false;
            }

            if (HasEntityGroup(entityGroupName))
            {
                return false;
            }

            var entityGroupGo = new GameObject(entityGroupName);
            entityGroupGo.transform.SetParent(transform);
            var entityGroup = entityGroupGo.AddComponent<EntityGroup>();
            entityGroup.Init(entityGroupName, 60, 20, 60, 1, _objectPoolMgr);
            _entityGroups.Add(entityGroupName, entityGroup);

            return true;
        }

        /// <summary>
        /// 显示entity
        /// </summary>
        /// <param name="entityId">唯一id</param>
        /// <param name="entityAssetName">路径</param>
        /// <param name="entityGroupName">类型</param>
        /// <param name="priority">优先级：值越大优先级越高</param>
        /// <param name="userData"></param>
        public void ShowEntity(int entityId, Type entityLogicType, string entityAssetName, string entityGroupName,
            int priority = 0, object userData = null)
        {
            if (HasEntity(entityId))
            {
                Log.Error("重复show entity  id=={0}", entityId);
                return;
            }

            if (IsLoadingEntity(entityId))
            {
                Log.Error("show entity  id=={0}正在加载中", entityId);
                return;
            }

            EntityGroup entityGroup = (EntityGroup) GetEntityGroup(entityGroupName);
            if (entityGroup == null)
            {
                Log.Error("show entity  id=={0} 不存在的entitygroup=={1}", entityId, entityGroupName);
                return;
            }

            var showEntityInfo = ShowEntityInfo.Create(entityLogicType, userData);
            var entityInstanceObject = entityGroup.SpawnEntityInstanceObject(entityAssetName);
            //var goInfo = GameObjectPool.GetGameObject(entityAssetName);
            if (entityInstanceObject == null)
            {
                var serialId = ++_serialId;
                _entitiesBeLoading.Add(entityId, serialId);
                _resourceMgr.LoadAssetAsync<GameObject>(entityAssetName, _loadAssetCallbacks,
                    InternalShowEntityInfo.Create(serialId, entityId, entityGroup, showEntityInfo));
                return;
            }

            InternalShowEntity(entityId, entityAssetName, entityGroup, (Object)entityInstanceObject.Instance, false, 0f,
                showEntityInfo);
        }

        public void HideEntity(int entityId, object userData = null)
        {
            if (IsLoadingEntity(entityId))
            {
                _entities2ReleaseOnLoad.Add(_entitiesBeLoading[entityId]);
                _entitiesBeLoading.Remove(entityId);
                return;
            }

            EntityInfo entityInfo = _GetEntityInfo(entityId);
            if (entityInfo == null)
            {
                return;
            }

            InternalHideEntity(entityInfo, userData);
        }

        //添加子entity
        public void AttachEntity(int childEntityId, int parentEntityId, object userData = null)
        {
            if (childEntityId == parentEntityId)
                return;
            EntityInfo childEntityInfo = _GetEntityInfo(childEntityId);
            if (childEntityInfo == null)
            {
                return;
            }

            if (childEntityInfo.Status >= EntityStatus.WillHide)
            {
                return;
            }

            EntityInfo parentEntityInfo = _GetEntityInfo(parentEntityId);
            if (parentEntityInfo == null)
                return;

            if (parentEntityInfo.Status >= EntityStatus.WillHide)
            {
                return;
            }

            Entity childEntity = childEntityInfo.Entity;
            Entity parentEntity = parentEntityInfo.Entity;
            DetachEntity(childEntityId, userData);
            childEntityInfo.ParentEntity = parentEntity;
            parentEntityInfo.AddChildEntity(childEntity);
            var attachEntityInfo = AttachEntityInfo.Create(parentEntity.transform, userData);
            parentEntity.OnAttached(childEntity, attachEntityInfo);
            childEntity.OnAttachTo(parentEntity, attachEntityInfo);
        }

        //解除子节点
        public void DetachEntity(int childEntityId, object userData = null)
        {
            EntityInfo childEntityInfo = _GetEntityInfo(childEntityId);
            if (childEntityInfo == null)
                return;
            Entity parentEntity = childEntityInfo.ParentEntity;
            if (parentEntity == null)
                return;
            EntityInfo parentEntityInfo = _GetEntityInfo(parentEntity.Id);
            if (parentEntityInfo == null)
                return;
            Entity childEntity = childEntityInfo.Entity;
            childEntityInfo.ParentEntity = null;
            parentEntityInfo.RemoveChildEntity(childEntity);
            parentEntity.OnDetached(childEntity, userData);
            childEntity.OnDetachFrom(parentEntity, userData);
        }

        //解除所有子实体
        public void DetachChildEntities(int parentEntityId, object userData = null)
        {
            EntityInfo parentEntityInfo = _GetEntityInfo(parentEntityId);
            if (parentEntityInfo == null)
                return;
            while (parentEntityInfo.ChildEntityCount > 0)
            {
                Entity childEntity = parentEntityInfo.GetChildEntity();
                DetachEntity(childEntity.Id, userData);
            }
        }

        public Entity GetParentEntity(int childEntityId)
        {
            EntityInfo childEntityInfo = _GetEntityInfo(childEntityId);
            if (childEntityInfo == null)
            {
                Log.Error("找不到id=={0}的entityInfo", childEntityId);
                return null;
            }

            return childEntityInfo.ParentEntity;
        }

        public Entity GetChildEntity(int parentEntityId)
        {
            EntityInfo parentEntityInfo = _GetEntityInfo(parentEntityId);
            if (parentEntityInfo == null)
            {
                Log.Error("找不到id=={0}的entityInfo", parentEntityId);
                return null;
            }

            return parentEntityInfo.GetChildEntity();
        }

        public void GetChildEntities(int parentEntityId, List<Entity> results)
        {
            EntityInfo parentEntityInfo = _GetEntityInfo(parentEntityId);
            if (parentEntityInfo == null)
            {
                Log.Error("找不到id=={0}的entityInfo", parentEntityId);
                return;
            }

            parentEntityInfo.GetChildEntities(results);
        }

        public bool HasEntity(int entityId)
        {
            return _entityInfos.ContainsKey(entityId);
        }

        public bool IsLoadingEntity(int entityId)
        {
            return _entitiesBeLoading.ContainsKey(entityId);
        }

        public EntityGroup GetEntityGroup(string entityGroupName)
        {
            if (string.IsNullOrEmpty(entityGroupName))
            {
                return null;
            }

            if (_entityGroups.TryGetValue(entityGroupName, out var entityGroup))
            {
                return entityGroup;
            }

            return null;
        }

        private EntityInfo _GetEntityInfo(int entityId)
        {
            if (_entityInfos.TryGetValue(entityId, out var entityInfo))
            {
                return entityInfo;
            }

            return null;
        }

        private bool HasEntityGroup(string entityGroupName)
        {
            if (_entityGroups.ContainsKey(entityGroupName))
            {
                return true;
            }

            return false;
        }

        private void InternalShowEntity(int entityId, string entityAssetName, EntityGroup entityGroup,
            Object entityInstance, bool isNewInstance, float duration, ShowEntityInfo userData)
        {
            Entity entity = CreateEntity(entityInstance, entityGroup, userData);
            if (entity == null)
            {
                _eventMgr.Fire(this,
                    ShowEntityFailureEventArgs.Create(entityId, entityAssetName, entityGroup.Name, "创建entity失败",
                        userData));
                return;
            }

            EntityInfo entityInfo = EntityInfo.Create(entity);
            _entityInfos.Add(entityId, entityInfo);
            entityInfo.Status = EntityStatus.WillInit;
            entity.OnInit(entityId, entityAssetName, entityGroup, isNewInstance, userData);
            entityInfo.Status = EntityStatus.Inited;
            entityGroup.AddEntity(entity);
            entityInfo.Status = EntityStatus.WillShow;
            entity.OnShow(userData);
            entityInfo.Status = EntityStatus.Showed;

            _eventMgr.Fire(this, ShowEntitySuccessEventArgs.Create(entity, duration, userData));
        }

        private void InternalHideEntity(EntityInfo entityInfo, object userData)
        {
            while (entityInfo.ChildEntityCount > 0)
            {
                Entity childEntity = entityInfo.GetChildEntity();
                HideEntity(childEntity.Id, userData);
            }

            if (entityInfo.Status == EntityStatus.Hidden)
            {
                return;
            }

            Entity entity = entityInfo.Entity;
            DetachEntity(entity.Id, userData);
            entityInfo.Status = EntityStatus.WillHide;
            entity.OnHide(userData);
            entityInfo.Status = EntityStatus.Hidden;

            EntityGroup entityGroup = (EntityGroup) entity.EntityGroup;
            if (entityGroup == null)
            {
                return;
            }

            entityGroup.RemoveEntity(entity);
            if (!_entityInfos.Remove(entity.Id))
            {
                return;
            }

            _eventMgr.Fire(this,
                HideEntityCompleteEventArgs.Create(entity.Id, entity.EntityPath, entity.EntityGroup, userData));

            _recycleQueue.Enqueue(entityInfo);
        }


        private void LoadAssetSuccessCallback(string entityAssetName, Object entityAsset, float duration,
            object userData)
        {
            InternalShowEntityInfo internalShowEntityInfo = (InternalShowEntityInfo) userData;
            if (internalShowEntityInfo == null)
            {
                return;
            }

            if (_entities2ReleaseOnLoad.Contains(internalShowEntityInfo.SerialId))
            {
                _entities2ReleaseOnLoad.Remove(internalShowEntityInfo.SerialId);
                ReferencePool.Release(internalShowEntityInfo);
                ReleaseEntity(entityAssetName, null);
                return;
            }

            _entitiesBeLoading.Remove(internalShowEntityInfo.EntityId);
            var entityInstance = InstantiateEntity(entityAsset);
            EntityInstanceObject entityInstanceInstanceObject =
                EntityInstanceObject.Create(entityAssetName, entityAsset, entityInstance, _resourceMgr);
            internalShowEntityInfo.EntityGroup.RegisterEntityInstanceObject(entityInstanceInstanceObject, true);

            InternalShowEntity(internalShowEntityInfo.EntityId, entityAssetName, internalShowEntityInfo.EntityGroup,
                entityInstance, true, duration, internalShowEntityInfo.UserData);
            ReferencePool.Release(internalShowEntityInfo);
        }

        private void LoadAssetFailureCallback(string assetName, string errorMessage, object userData)
        {
            InternalShowEntityInfo internalShowEntityInfo = (InternalShowEntityInfo) userData;
            if (internalShowEntityInfo == null)
            {
                return;
            }

            if (_entities2ReleaseOnLoad.Contains(internalShowEntityInfo.SerialId))
            {
                _entities2ReleaseOnLoad.Remove(internalShowEntityInfo.SerialId);
                return;
            }

            _entitiesBeLoading.Remove(internalShowEntityInfo.EntityId);
            _eventMgr.Fire(this,
                ShowEntityFailureEventArgs.Create(internalShowEntityInfo.EntityId, assetName,
                    internalShowEntityInfo.EntityGroup.Name, "加载entity资源失败", internalShowEntityInfo.UserData));
        }
        
        private Object InstantiateEntity(Object entityAsset)
        {
            return Instantiate(entityAsset);
        }

        private Entity CreateEntity(Object entityInstance, EntityGroup entityGroup, object userData)
        {
            GameObject instance = entityInstance as GameObject;
            if (instance == null)
            {
                Log.Error("Entity instance is invalid.");
                return null;
            }

            Transform tran = instance.transform;
            tran.SetParent(entityGroup.Root.transform);
            var entity = instance.GetComponent<Entity>();
            if (entity == null)
            {
                entity = instance.AddComponent<Entity>();
            }

            return entity;
        }

        private void ReleaseEntity(string assetPath, Object entityInstance)
        {
            _resourceMgr.UnloadAsset(assetPath);
            if (entityInstance != null)
            {
                Destroy(entityInstance);
            }
        }
    }
}
